package com.fee.xmediaplayer.demo

import android.content.Context
import android.net.Uri
import android.text.TextUtils
import android.util.Log
import android.view.Surface
import android.view.SurfaceHolder
import tv.danmaku.ijk.media.player.AndroidMediaPlayer
import tv.danmaku.ijk.media.player.IMediaPlayer
import tv.danmaku.ijk.media.player.IjkMediaPlayer
import tv.danmaku.ijk.media.player.MediaInfo
import tv.danmaku.ijk.media.player.misc.IMediaDataSource
import tv.danmaku.ijk.media.player.misc.ITrackInfo
import java.io.FileDescriptor
import java.lang.ref.WeakReference
import java.util.*
import kotlin.Exception

/**
 * 全局复用的 单例 媒体播放器
 */
class SingleMediaPlayer private constructor(){
    init {
        IjkMediaPlayer.loadLibrariesOnce(null);
        IjkMediaPlayer.native_profileBegin("libijkplayer.so")
    }

    private val TAG = "SingleMediaPlayer"

    /**
     * 当前播放器的状态
     */
    private var mPlayerState = ""
    /**
     * 是否使用的是 外部的 播放器实例
     */
    private var mIsUsedOutSidePlayer = false

    /**
     * 当前的 具体 播放器的实现 实例
     */
    private var mCurMediaPlayer: IMediaPlayer? = null
    set(value) {
        field = value
        Log.i(TAG,"--> setCurMediaPlayer() value = $value")
        bindMediaPlayerListeners()
    }

    /**
     * 当前所 添加进来的 播放器监听者；为了支持本 单例播放器对应多个 监听者
     */
    private var mPlayerListeners: WeakHashMap<IPlayerListener, String>? = null



    /**
     * 播放器的 状态监听者
     */
    private val mPlayerListener by lazy {
        object :IPlayerListener{
            /**
             * 播放器已经 准备/加载资源 就绪的回调
             * @param player 当前播放器
             */
            override fun onPrepared(player: IMediaPlayer) {
                this@SingleMediaPlayer.onPrepared(player)
            }

            /**
             * 播放器 播放完成的回调
             * @param player 当前播放器
             */
            override fun onCompletion(player: IMediaPlayer) {
                this@SingleMediaPlayer.onCompletion(player)
            }

            /**
             * 播放器 错误信息的 回调
             * @param player 当前播放器
             * @param what 错误类型
             * @param extra 额外信息
             * @return true: 最后会中断播放器的默认处理；false: 不中断播放器默认的处理,即播放器会继续回调 [onCompletion]
             */
            override fun onError(player: IMediaPlayer, what: Int, extra: Int): Boolean {
                return this@SingleMediaPlayer.onError(player,what,extra)
            }
        }
    }

    companion object Builder {
        /**
         * 播放器状态：正在准备
         */
        const val PLAYER_STATE_PREPARING = "preparing"

        /**
         * 播放器状态：已准备好
         */
        const val PLAYER_STATE_PREPARED = "prepared"

        /**
         * 播放器状态：正在播放
         */
        const val PLAYER_STATE_PLAYING = "playing"

        /**
         * 播放器状态：暂停
         */
        const val PLAYER_STATE_PAUSED = "paused"

        /**
         * 播放器状态：停止
         */
        const val PLAYER_STATE_STOPPED = "stopped"

        /**
         * 播放器状态：播放完成
         */
        const val PLAYER_STATE_COMPLETED = "completed"

        /**
         * 播放器状态：重置
         */
        const val PLAYER_STATE_RESET = "reset"

        /**
         * 播放器状态：已释放
         */
        const val PLAYER_STATE_RELEASED = "released"

        /**
         * 播放器状态：播放异常
         */
        const val PLAYER_STATE_ERROR = "error"

        /**
         * 空媒体路径
         */
        const val PATH_OF_EMPTY_MEDIA = "empty.media.path"

        val player by lazy {
            SingleMediaPlayer()
        }

        /**
         * 外部指定 一个 MediaPlayer 实现 实例
         */
        fun assignMediaPlayerImp(theMediaPlayerImp: IMediaPlayer?) {
            player.assignMediaPlayer(theMediaPlayerImp)
        }
    }

    private val mDefMediaPlayerImp by lazy {
        //    java.lang.UnsatisfiedLinkError:
        //    dalvik.system.PathClassLoader[DexPathList[[zip file
        //    "/data/app/com.fee.xmediaplayer.demo-Uk75N_swu88DRkhm78V1oQ==/base.apk"]
        //    ,nativeLibraryDirectories=[/data/app/com.fee.xmediaplayer.demo-Uk75N_swu88DRkhm78V1oQ==/lib/arm64, /system/lib64, /product/lib64, /prets/lib64]]]
        //    couldn't find "libijkffmpeg.so"
        IjkMediaPlayer()
//        AndroidMediaPlayer()
    }


    /**
     * 外部 指定/分配 的播放器具体实现
     */
    fun assignMediaPlayer(theMediaPlayerImp: IMediaPlayer?) {
        mIsUsedOutSidePlayer = theMediaPlayerImp != null
        mCurMediaPlayer = theMediaPlayerImp ?: mDefMediaPlayerImp
    }


    /**
     * 给播放器 设置 显示/渲染 的 [SurfaceHolder]
     * @param surfaceHolder 注意：该 [SurfaceHolder] 需要在创建后才设置
     */
    fun setDisplay(surfaceHolder: SurfaceHolder?) {
//        surfaceHolder?.surface
        Log.d(TAG,"--> setDisplay() surfaceHolder = $surfaceHolder")
//        curMediaPlayer().setDisplay(surfaceHolder)
        setSurface(surface = surfaceHolder?.surface)
    }

    private var mCurSurfaceWeakRef: WeakReference<Surface>? = null
    /**
     * 给播放器设置 显示/渲染 的 [Surface]
     * @param surface 注意：该 [surface] 需要在创建后才设置
     */
    fun setSurface(surface: Surface?) {
        Log.d(TAG,"--> setSurface() surface = $surface")
        curMediaPlayer().setSurface(surface)
    }

    fun setDataSource(context: Context, dataUri: Uri?) {
        setDataSource(context,dataUri,null)
    }

    fun setDataSource(context: Context, dataUri: Uri?, headers: MutableMap<String, String>?) {
        try {
            curMediaPlayer().setDataSource(context, dataUri, headers)
        } catch (ex: Exception) {
            Log.e(TAG,"--> setDataSource() with dataUri = $dataUri,and headers , occur: $ex")
        }
    }

    fun setDataSource(fd: FileDescriptor) {
        try {
            curMediaPlayer().setDataSource(fd)
        } catch (ex: Exception) {
            Log.e(TAG,"--> setDataSource() with fileDescriptor  = $fd, occur: $ex")
        }
    }

    fun setDataSource(mediaPath: String?) {
        try {
            curMediaPlayer().dataSource = mediaPath
        } catch (ex: Exception) {
            Log.e(TAG,"--> setDataSource() with mediaPath  = $mediaPath, occur: $ex")
        }
    }

    fun setDataSource(mediaDataSource: IMediaDataSource?) {
        try {
            curMediaPlayer().setDataSource(mediaDataSource)
        } catch (ex: Exception) {
            Log.e(TAG,"--> setDataSource() with mediaDataSource  = $mediaDataSource, occur: $ex")
        }
    }

    fun play(mediaPath: String?) {
        if (mediaPath.isNullOrBlank()) {
            return
        }
        val lastMediaSource = getDataSource()
        Log.d(TAG,"--> play() mediaPath = $mediaPath, last source = $lastMediaSource")
        if (mediaPath == lastMediaSource) {//将播放的是同一个 媒体资源

        } else {//播放的是不同的媒体 资源
            if (!lastMediaSource.isNullOrBlank()) {
                stop()
            }
        }
        setDataSource(mediaPath)
        prepareAsync()
    }

    fun getDataSource(): String? {
        return curMediaPlayer().dataSource
    }

    /**
     * 异步准备
     */
    private fun prepareAsync() {
        Log.d(TAG,"--> prepareAsync()")
        try {
            curMediaPlayer().prepareAsync()//java.lang.IllegalStateException
            notifyPlayerStateChanged(PLAYER_STATE_PREPARING)
        } catch (ex: Exception) {
            Log.e(TAG,"--> prepareAsync() : $ex")
        }
    }

    /**
     * 开始播放，如果是正常的 播放完成，则会重新播放
     */
    fun start() {
        Log.d(TAG,"--> start()")
        try {
            curMediaPlayer().start()
            notifyPlayerStateChanged(PLAYER_STATE_PLAYING)
        } catch (ex: Exception) {
            Log.e(TAG,"--> start() occur: $ex")
        }
    }

    fun stop() {
        Log.d(TAG,"--> stop()")
        try {
            curMediaPlayer().stop()
            notifyPlayerStateChanged(PLAYER_STATE_STOPPED)
        } catch (ex: Exception) {
            Log.e(TAG,"--> stop() occur: $ex")
        }
    }

    /**
     * 暂停播放
     */
    fun pause() {
        Log.d(TAG,"--> pause()")
        try {
            curMediaPlayer().pause()
            notifyPlayerStateChanged(PLAYER_STATE_PAUSED)
        } catch (ex: Exception) {
            Log.e(TAG,"--> pause() occur: $ex")
        }
    }

    /**
     * 设置在播放的过程中 是否需要让 屏幕 常亮
     * @param isNeedOnOrNot true: 需要常亮;
     */
    fun setScreenOnWhilePlaying(isNeedOnOrNot: Boolean) {
        curMediaPlayer().setScreenOnWhilePlaying(isNeedOnOrNot)
    }

    /**
     * 获取当前所播放的视频的 宽
     */
    fun getVideoWidth(): Int {
        return curMediaPlayer().videoWidth
    }

    /**
     * 获取当前所播放的视频的 高
     */
    fun getVideoHeight(): Int {
        return curMediaPlayer().videoHeight
    }

    /**
     * 获取当前播放器是否正在播放
     */
    fun isPlaying(): Boolean {
        return curMediaPlayer().isPlaying
    }

    /**
     * 跳转 到 目的 时长(毫秒) 进行播放
     * @param msec 目标时长 毫秒
     */
    fun seekTo(msec: Long) {
        Log.d(TAG,"--> seekTo() msec = $msec")
        try {//throws IllegalStateException
            curMediaPlayer().seekTo(msec)
        } catch (ex: Exception) {
            Log.e(TAG, "--> seekTo() with msec = $msec, occur: $ex")
        }
    }

    /**
     * 获取 当前正在播放 的时长位置
     */
    fun getCurrentPosition(): Long {
        return curMediaPlayer().currentPosition
    }

    /**
     * 获取当前正在播放的媒体的总时长
     */
    fun getDuration(): Long {
        return curMediaPlayer().duration
    }

    /**
     * 播放器释放
     * [IjkMediaPlayer] 调用释放后就不能再播放了
     *  this.stayAwake(false);
    this.updateSurfaceScreenOn();
    this.resetListeners();
    this._release();
     */
    fun release() {
        Log.i(TAG,"--> release()")
        curMediaPlayer().release()
        mCurMediaPlayer = null
        notifyPlayerStateChanged(PLAYER_STATE_RELEASED)
    }

    /**
     * 播放器重置
     *  this.stayAwake(false);
    this._reset();
    this.mEventHandler.removeCallbacksAndMessages((Object)null);
    this.mVideoWidth = 0;
    this.mVideoHeight = 0;
     */
    fun reset() {
        curMediaPlayer().reset()
        notifyPlayerStateChanged(PLAYER_STATE_RESET)
        Log.i(TAG,"--> reset()")
    }

    /**
     * 设置播放器 左、右音频通道的 音量
     * @param leftVolume 左音频通道目标音量
     * @param rightVolume 右音频通道目标音量
     */
    fun setVolume(leftVolume: Float, rightVolume: Float) {
        curMediaPlayer().setVolume(leftVolume, rightVolume)
    }

    /**
     * 获取当前 的 AudioSessionId
     */
    fun getAudioSessionId(): Int {
        return curMediaPlayer().audioSessionId
    }

    /**
     * 获取当前播放的媒体 信息
     * @return [MediaInfo]
     */
    fun getMediaInfo(): MediaInfo? {
        return curMediaPlayer().mediaInfo
    }

    /**
     * 配置当前是否开启 log 输出
     * @param enable true:开启；
     */
    fun setLogEnabled(enable: Boolean) {
        curMediaPlayer().setLogEnabled(enable)
    }

    /**
     * 获取当前 是否 可播放状态
     * @return true: 可播放
     */
    fun isPlayable(): Boolean {
        return curMediaPlayer().isPlayable
    }

    /**
     *配置 当前的 音频 类型
     * @param streamtype 音频类型
     */
    fun setAudioStreamType(streamtype: Int) {
        curMediaPlayer().setAudioStreamType(streamtype)
    }

    /**
     * 是否开启在 后台 播放
     */
    fun setKeepInBackground(enableKeepInBackground: Boolean) {
        curMediaPlayer().setKeepInBackground(enableKeepInBackground)
    }

    fun getVideoSarNum(): Int {
        return curMediaPlayer().videoSarNum
    }

    fun getVideoSarDen(): Int {
        return curMediaPlayer().videoSarDen
    }

    /**
     * 设置唤醒模式
     */
    fun setWakeMode(context: Context, mode: Int) {
        curMediaPlayer().setWakeMode(context, mode)
    }

    /**
     * 设置是否 需要 循环播放
     */
    fun setLooping(looping: Boolean) {
        curMediaPlayer().isLooping = looping
    }

    /**
     * 获取当前是否处于 循环播放状态
     */
    fun isLooping(): Boolean {
        return curMediaPlayer().isLooping
    }

    /**
     * 获取当前 的 track infos 音、视频轨道信息
     */
    fun getTrackInfo(): Array<ITrackInfo>? {
        return curMediaPlayer().trackInfo
    }



    /**
     * 获取当前的媒体播放器 实例
     */
    fun curMediaPlayer(): IMediaPlayer {
        if (mCurMediaPlayer == null) {
            mCurMediaPlayer = if (PLAYER_STATE_RELEASED == mPlayerState) {
                //已经释放了
                if (mIsUsedOutSidePlayer) {//是使用的外部指定的播放器
                    mDefMediaPlayerImp
                } else {
                    getDefMediaPlayer()
                }
            } else{
                mDefMediaPlayerImp
            }
            mIsUsedOutSidePlayer = false
        }
        return mCurMediaPlayer ?: mDefMediaPlayerImp
    }

    private fun getDefMediaPlayer() = IjkMediaPlayer()
    /**
     * 添加播放器的监听者
     */
    fun addPlayerListener(listener: IPlayerListener) {
        if (mPlayerListeners == null) {
            mPlayerListeners = WeakHashMap(2)
        }
        val listenerTag = listener.javaClass.simpleName
        mPlayerListeners?.put(listener,listenerTag)
    }

    /**
     * 移除播放器的监听者
     */
    fun removePlayerListener(listener: IPlayerListener?) {
        if (listener == null) {
            return
        }
        mPlayerListeners?.remove(listener)
    }


    private fun bindMediaPlayerListeners() {
        Log.i(TAG,"--> bindMediaPlayerListeners() ")
        mCurMediaPlayer?.run {
            setOnPreparedListener(mPlayerListener)
            setOnCompletionListener(mPlayerListener)
            setOnErrorListener(mPlayerListener)
            setOnInfoListener(mPlayerListener)
            setOnBufferingUpdateListener(mPlayerListener)
            setOnSeekCompleteListener(mPlayerListener)
            setOnVideoSizeChangedListener(mPlayerListener)
        }
    }


    //--------------- 播放器的 监听回调 @start -------------------
    /**
     * 播放器已经 准备/加载资源 就绪的回调
     * @param player 当前播放器
     */
    private fun onPrepared(player: IMediaPlayer) {
        Log.i(TAG,"--> onPrepared() ")
        notifyPlayerStateChanged(PLAYER_STATE_PREPARED) // ijk 播放器 在这回调之后直接 播放了
        notifyPlayerStateChanged(PLAYER_STATE_PLAYING) // ijk 播放器 在这回调之后直接 播放了
        mPlayerListeners?.forEach {
            it.key.onPrepared(player)
        }
    }

    /**
     * 播放器 播放完成的回调
     * @param player 当前播放器
     */
    private fun onCompletion(player: IMediaPlayer) {
        Log.i(TAG,"--> onCompletion() ")
        notifyPlayerStateChanged(PLAYER_STATE_COMPLETED)
        mPlayerListeners?.forEach {
            it.key.onCompletion(player)
        }
    }

    /**
     * 播放器 错误信息的 回调
     * @param player 当前播放器
     * @param what 错误类型
     * @param extra 额外信息
     * @return true: 最后会中断播放器的默认处理；false: 不中断播放器默认的处理,即播放器会继续回调 [onCompletion]
     */
    private fun onError(player: IMediaPlayer, what: Int, extra: Int): Boolean {
        Log.e(TAG, "--> onError() what = $what, extra = $extra")
        notifyPlayerStateChanged(PLAYER_STATE_ERROR)
        var isHandledOnError = true
        mPlayerListeners?.forEach {
            val aListener = it.key
            val tag = it.value
            val isHandled = aListener.onError(player, what, extra)
            if (isHandled) {//只要有一个 监听者处理了，即认为处理了，则播放器不会再回调 [onCompletion]
                isHandledOnError = true
                Log.i(TAG, "--> onError() the listener handled error, tag of listener is $tag")
            }
        }
        return isHandledOnError
    }

    //--------------- 播放器的 监听回调 @end -------------------

    private fun notifyPlayerStateChanged(curPlayerState: String) {
        mPlayerState = curPlayerState
    }
}